Задан набор целых чисел a1, a2, ..., an.
Для заданного числа x найдите
такое ai, что x xor ai максимально.
Вход. Первая
строка содержит количество чисел n (n ≤ 105) и количество
запросов q. Вторая строка содержит
целые числа a1, a2, ..., an (0 ≤ ai
≤ 1018). Каждая из следующих q строк содержит одно число x (0 ≤ x ≤ 1018).
Выход. Для
каждого значения x выведите
в отдельной строке такое значение ai,
для которого x xor ai максимально.
Пример
входа |
Пример
выхода |
5 6 5 3 7 2 6 1 2 4 5 3 6 |
6 5 3 2 5 3 |
бор
Анализ алгоритма
Занесем все
входные числа a1, a2, ..., an в бинарный бор. Каждая вершина
бора имеет два сына, которые соответствуют битам 0 и 1. Поскольку ai ≤ 1018, 260 > 1018, то каждое ai содержит не
более 61 бита (которые
нумеруются от 0 до 60).
Теперь покажем как для заданного x найти такое ai, что x xor ai максимально. Пусть x = b1b2…bn – двоичное представление числа. Если существует такое i что ai = (через обозначен инвертированный
бит bk), то двоичное представление x xor ai будет состоять из n единиц и является наибольшим из
возможных. Двигаемся по бору последовательно по битам . Если для некоторого k в боре пути по
биту нет, то двигаемся по
биту bk. В конце
прохода по битам x прийдем в вершину, которой соответствует искомое ai.
Пример
Числа во входном
примере содержат не более 3 бит. Построим бинарный бор, где каждое число
представлено тремя битами.
Пусть x = 6 = 1102. Инвертируем
биты: inv(1102)
= 0012. Двигаемся по бору:
·
первый шаг: бит 0 в боре есть;
·
второй шаг: бита 0 в боре нет, двигаемся по 1;
·
третий шаг: бит 1 в боре есть;
Мы пришли к
числу ai = 3.
Реализация алгоритма
Объявим
бинарный бор trie.
В переменной TrieSize храним его размер.
#define MAX 60 *
100000
int trie[MAX][2];
int TrieSize;
Пусть
все биты числа ai вставлены в бор. Если число ai заканчивается в вершине номер v бора, то установим num[v] = ai. Таким образом для любой вершины
бора мы будем знать, конечная ли она для некоторого ai, и если ответ утвердительный, то
сможем получить и само значение ai.
long long num[MAX];
Функция
Insert вставляет число x в бор. Биты вставляем в порядке от старшего к младшему.
void Insert(long long x)
{
int i, v
= 0;
for (i =
60; i >= 0; i--)
{
Извлекаем
i-ый бит
числа x в переменную bit.
int bit
= x & (1LL << i) ? 1 : 0;
Если
пути по биту bit в боре нет, то создаем новую вершину.
if
(trie[v][bit] == -1)
trie[v][bit]
= TrieSize++;
Двигаемся
по бору дальше.
v =
trie[v][bit];
}
Вершина
v – конечная для числа x. Сохраним эту информацию.
num[v] = x;
}
Функция
Find(x)
возвращает такое ai, для которого x xor ai максимально.
long long
Find(long long x)
{
int i, v
= 0;
for (i =
60; i >= 0; i--)
{
Двигаемся
по бору по битам, реверсным к двоичному представлению числа x.
int bit
= x & (1LL << i) ? 0 : 1; // reverese bit
Если
движение по бору невозможно, двигаемся по другому биту.
if
(trie[v][bit] == -1) bit ^= 1;
v =
trie[v][bit];
}
Возвращаем
значение ai, соответствующее вершине бора v.
return
num[v];
}
Основная
часть программы. Читаем входные данные. Изначально бор содержит одну вершину (TrieSize = 1).
scanf("%d %d",
&n, &q);
TrieSize = 1;
memset(trie, -1, sizeof(trie));
memset(num, -1, sizeof(num));
Вставляем
числа входной последовательности a1, a2, ..., an в бинарный бор.
for (i = 0;i < n; i++)
{
scanf("%lld",
&x);
Insert(x);
}
Обрабатываем q запросов. Для каждого входного значения x ищем такое значение ai,
что x xor ai максимально.
for (i = 0;i < q; i++)
{
scanf("%lld",
&x);
x =
Find(x);
printf("%lld\n", x);
}